
package SSF.OS.TCP.test;

/*
 * tcpClientSession.java
 */

import SSF.OS.*;
import SSF.OS.Socket.*;
import SSF.OS.TCP.*;
import SSF.Net.*;
import SSF.Net.Util.*;
import SSF.Util.Streams.*;
import com.renesys.raceway.DML.*;
import java.io.*;
import java.util.*;

/** A simple prototype class executing one TCP client-server session;
 *  it is instantiated by SSF.OS.TCP.test.tcpClient.
 */
public class tcpClientSession extends ProtocolSession {
  public static final int CLOSED     = 0x0;
  public static final int CONNECTING = 0x1;
  public static final int REQUESTING = 0x2;
  public static final int READING    = 0x4;
  public static final int CLOSING    = 0x8;

  public static final String stateString(int s) {
	  switch(s) {
	    case CLOSED: return "CLOSED";
	    case CONNECTING: return "CONNECTING";
	    case REQUESTING: return "REQUESTING";
	    case READING: return "READING";
	    case CLOSING: return "CLOSING";
	    default: return "?UNKNOWN:"+s;
	  }
  }

  /************************* Class Variables *********************/

  /** client who created this session */
  public tcpClient owner;

  /** client-assigned session id */
  public int id;

  /** current session state **/
  public int state = CLOSED;

  /** client's Continuation for this session */
  public Continuation clientCont;

  /** print out debug information */
  public boolean showDebug;

  /** print out summary session report */
  public boolean showReport;

  /** size (in bytes) of request datagram sent from client to server */
  public int requestSize;

  /** requested file size */
  public int fileSize;
  // long fileSize;  bugfix 11/4/00 ato

  /** Host entity where this pseudo-protocol is installed */
  public Host localHost;

  /** local socket used by this session */
  public socketAPI sd;

  private String srvNHI;    
  private int srvIP;
  private int srvPort;

  private String locNHI;
  private int locIP;
  private int locPort;
  private long dataReceived = 0L;
  private String str;
  private long startTime;

  /************************* Constructors ************************/

  public tcpClientSession (tcpClient client, int sessid,
                           tcpClient.serverData serv, int file_size) {
    owner = client;
    localHost = client.localHost;
    id = sessid;
    locNHI = client.localNHI;
    locIP = client.localIP;

    requestSize = client.request_size;
    fileSize = file_size;
    showReport = client.show_report;
    showDebug = client.debug;

    srvNHI = serv.nhi;
    srvIP  = serv.ip;
    srvPort = serv.port;

  }

  /************************* Class Methods ***********************/

  public void begin(Continuation caller) {
    clientCont = caller;
    begin();
  }

  private void begin() {
    startTime = localHost.now();

    state=CLOSED;
    
    try { 
      sd = owner.sockms.socket(this, "tcp");
      clientDebug(0," connect()");
      state=CONNECTING;
      sd.connect(srvIP, srvPort, new Continuation() {
        public void success() {  // tcp opened connection to server
          sendRequest();
        }
        public void failure(int errno) {
	  state=CLOSED;
	  //clientDebug(errno, " connect() FAILURE");
	  clientCont.failure(errno);
        }
      });
    } catch (ProtocolException e) {
      System.err.println("tcpClient: " + e);
    }
  }

  void sendRequest() {
    state=REQUESTING;
    Object[] obj = new Object[1];
    obj[0] = new Integer(fileSize);
    sd.write(obj, requestSize, new Continuation() {
	public void success() {
	  clientDebug(0,(" request " + fileSize + "B to " + srvNHI + " OK"));
	  readData();
	}
	public void failure(int errno) {
	  state=CLOSED;
	  clientDebug(errno, ("request to " + srvNHI));
	}
      });
  }

  void readData() {
    state=READING;
    sd.read(fileSize, new Continuation() {
      public void success() {
        if(showReport || showDebug) {
          double duration = (localHost.now() -
                            startTime)/(double)SSF.Net.Net.seconds(1.0);
          str = " rcvd " + fileSize + "B at "
                +  ((long)(8*fileSize/duration))/1000.0
                + "kbps - read() SUCCESS ";
          sessionReport(0,str);
        }
	state=CLOSING;
        try {
          sd.close(new Continuation() {
            public void success() {
	      clientDebug(0," close() OK");
	      state=CLOSED;
            }
            public void failure(int errno) {
	      clientDebug(errno, " close() FAILURE");
	      state=CLOSED;
            }
          });
        } catch (ProtocolException e) {
            System.err.println(e);
        }
        if(clientCont != null)
          clientCont.success();
      }
      public void failure(int errno) {
	clientDebug(errno, " read() FAILURE");
	state=CLOSED;
      }
    });
  }

  /** preamble to client-side-only diagnostics */
  public void clientDebug(int errno, String str){
    if (!showDebug) return;

    StringBuffer lineBuffer = new StringBuffer(200); 
    lineBuffer.append(localHost.now()/(double)SSF.Net.Net.seconds(1.0))
      .append(" "+owner.name+" ").append(locNHI).append(" ")
      .append(IP_s.IPtoString(locIP)).append(" sid ").append(id).append(str);

    if(errno>0)
      lineBuffer.append(" "+socketMaster.errorString(errno));
    System.err.println(lineBuffer);
  }
  /** preamble to end2end session diagnostics */
  public void sessionReport(int errno, String str){
    StringBuffer lineBuffer = new StringBuffer(200); 
    lineBuffer.append(localHost.now()/(double)SSF.Net.Net.seconds(1.0))
      .append(" [ sid ").append(id).append(" start ") 
      .append(startTime/(double)SSF.Net.Net.seconds(1.0))
      .append(" ] "+owner.name+" ").append(locNHI).append(" srv ")
      .append(srvNHI).append(str);

    if(errno>0)
      lineBuffer.append(" "+socketMaster.errorString(errno));
    System.err.println(lineBuffer);
  }

  public boolean push(ProtocolMessage message, ProtocolSession fromSession)
    throws ProtocolException {
    return false;
  }

}
